package eraserhead.entity;

import java.util.ArrayList;
import java.util.List;

/**
 * Entity class for Decision Lines Event
 * @author Quanquan
 * @author Greg
 * @author Kevin
 */
public abstract class Decision {
	// I didn't follow the g.design exactly
	// Combined g.design and everyone else's a.impl
	String id;
	String question;
	int users; //Should become an int once we're ready to change everything
	User signedInAs;  // the user who is using this client
	int userPlacement;
	List<Line> lines = new ArrayList<Line>();
	String errorMsg = "";
	List<Edge> edges = new ArrayList<Edge>(); // Using this because it makes drawing edges easier.
	//boolean isModerator; if no longer storing a list of users perhaps status of current user should be stored here
	
	/** Assume users can join an newly created Decision by default */
	boolean canJoin = true;
	boolean hasStarted = false;
	boolean formSent = true;
	
	int currentUser = 0;
	int numUsers;
	private int numRounds;
	
	final int minEdgeHeightDifference = 7;
	
	/**
	 * Create a new {@link Decision}
	 * @param numRounds Number of rounds
	 * @param numUsers Number of users
	 */
	public Decision(int numRounds, int numUsers) {
		this.question = "";
		this.setNumRounds(numRounds);
		this.numUsers = numUsers;
		for (int i = 0; i < numUsers; i++) {
			lines.add(new Line(i));
		}
	}
	
	/**
	 * Determine the priority based on the edges
	 * @return  List of string representing the prioritized list of choices
	 */
	public String[] prioritizedChoices() {
		String[] result = new String[numUsers];
		for (int i = 0; i < this.numUsers; i++) {
			int currentPosition = i;
			Line currentLine = this.lines.get(currentPosition);
			int currentHeight = -1;
			while (currentLine.nextEdge(currentHeight) != null) {
				if (currentPosition == currentLine.nextEdge(currentHeight).first) {
					currentPosition++;
				} else {
					currentPosition--;
				}
				currentHeight = currentLine.nextEdge(currentHeight).height;
				currentLine = this.lines.get(currentPosition);
			}
			result[currentPosition] = this.lines.get(i).choice;
		}
		return result;
	}
	
	public String finish()
	{
		String[] results = prioritizedChoices();
		String finalinput = "<html> <b>Final results:</b> <br />" + "<ol>";
		for(int i = 0; i < results.length; i++)
		{
			finalinput = finalinput + "<li>" + results[i] + "</li>";
		}
		finalinput = finalinput + "</ol>" + "</html>";
		return finalinput;
	}
	/**
	 * User sign in to a Decision Lines Event
	 * @return  True if successful; false if not
	 */
	public boolean userJoin(User isUser) {
/*		if (canJoin) {
			if (name.length() == 0) {
				return false;
			}
			for (int i = 0; i < users; i++) {
				if (users.get(i).name.equals(name)) {
					signedInAs = users.get(i) ;
					return users.get(i).password.equals(password);
				}
			}
			User newUser = new User(name, password, isModerator);
			users.add(newUser);
			signedInAs = newUser;
			updateHashMap();
			return true;
		} else {
			return false;
		}
*/		
	  	if(users<numUsers)
	  	{
	  		users++; 
		  	if(isUser != null) {
		  		userPlacement = users;
		  		userPlacement--;
		  		signedInAs = isUser;
		  	}
		  	return true;
		}
		else
		{
		  	return false;
		} 
	}
	
	public void resetLines() {
		List<Line> newLines = new ArrayList<Line>();
		int i = 0;
		for (Line l : lines) {
			if (l.choice.length() > 0) {
				Line newLine = new Line(i);
				newLine.choice = l.choice;
				newLines.add(newLine);
				i++;
			}
		}
		//if (newLines.size() >= 3) {
		lines = newLines;
		/*} else {
			errorMsg = "Need more than 3 lines.";
			return false;
		}*/
	}
	
	/**
	 * Determines whether the edge is valid
	 * @param e  The edge to be determined
	 * @return  true if valid; false if not
	 */
	public boolean isValidEdge(Edge e) {
		Line leftLine = lines.get(e.first);
		for (Edge other : leftLine.edges) {
			int diff = Math.abs(other.height - e.height);
			if (diff < minEdgeHeightDifference) {
				errorMsg = "Invalid Edge. Please try again.";
				return false;
			}
		}
		Line rightLine = lines.get(e.second);
		for (Edge other : rightLine.edges) {
			int diff = Math.abs(other.height - e.height);
			if (diff < minEdgeHeightDifference) {
				errorMsg = "Invalid Edge. Please try again.";
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Associate an edge with appropriate lines
	 * @param e  The edge to be added
	 * @return  true if edge successfully added; false otherwise
	 */
	public boolean addEdge(Edge e) {
		if (!isValidEdge(e)) {
			return false;
		} else {
			lines.get(e.first).edges.add(e);
			lines.get(e.second).edges.add(e);
			edges.add(e); // Add it to the "master list" of edges... for drawing's sake!
			return true;
		}
	}

	public boolean setChoice(int position, String value) {
		for (Line l : lines) {
			if (l.position == position) {
				l.setChoice(value);
				return true;
			}
		}
		return false;
	}

	/**
	 * Set the choice of a certain line
	 * @param position  The position of the line wanted
	 * @param value  The new choice to be set
	 * @return  true if successful; false if not
	 */
	public boolean isValidChoice(int position, String value) {
		if (value.length() == 0) {
			errorMsg = "Empty choice. Please try again.";
			return false;
		}
		for (Line l : lines) {
			if (l.choice.equals(value) && l.position != position) {
				errorMsg = "Duplicate Choice. Please try again.";
				return false;
			}
		}
		//lines.get(position).setChoice(value);
		return true;
	}
	
	abstract public void updateHashMap();
	abstract public boolean[] userGiveChoice();
	abstract public boolean isOpen();
	
	/**
	 * Determine whether the program is done
	 * @return  true if yes; false if not
	 */
	// why is counter == numUsers * numRounds * 2?  Specifically, why *2?
	// because one edge is in two lines, so it's counted twice
	public boolean isDone() {
		int counter = 0;
		for (Line l : lines) {
			counter = counter + l.edges.size();
		}
		if (counter == users * numRounds * 2) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Determine whether there are more than three choices
	 * @return  true if all filled; false, otherwise 
	 */
	public boolean isReady() {
		int choiceCounter = 0;
		for (Line l : lines) {
			if (l.choice.length() > 0) {
				choiceCounter++;
			}
		}
		return choiceCounter >= 3;
	}
	
	public boolean usersAllHere() {
		return users == numUsers;
	}
	
	public void resetNumUsers() {
		numUsers = users;
	} 
	
	/**
	 * Get the number of lines
	 * @return  The number of lines
	 */
	public int getNumUsers() {
		return numUsers;
	}

	public void setNumUsers(int numUsers) {
		this.numUsers = numUsers;
	}

	/**
	 * Get the current user
	 * @return  The current user
	 */
	public int getCurrentUser() {
		return currentUser;
	}
	
	/**
	 * Sets the current user index.
	 * @param newCurrentUser
	 */

	public void setCurrentUser(int newCurrentUser)
	{
		currentUser = newCurrentUser;
	}
	
	
	/**
	 * Get the question of this decision
	 * @return  The question of this decision
	 */
	public String getQuestion() {
		return question;
	}

	/**
	 * Set the question of this decision to a new one
	 * @param question  The question to be set
	 */
	public void setQuestion(String question) {
		this.question = question;
	}
	
	/**
	 * Set if user can join this event
	 * @param canJoin  True if yes; false if not
	 */
	public void setCanJoin(boolean canJoin) {
		this.canJoin = canJoin;
	}
	
	
	
	/**
	 * Get the error message
	 * @return  The error message
	 */
	public String getErrorMsg() {
		return errorMsg;
	}

	public int getNumRounds() {
		return numRounds;
	}

	public void setNumRounds(int numRounds) {
		this.numRounds = numRounds;
	}
	
	public List<Line> getLines() {
		return lines;
	}
	
	public List<Edge> getEdges() {
		return edges;
	}
	
	public User getSignedInAs() {
		return signedInAs;
	} 
	
	public int getUsers() {
		return users;
	}
	
	public int getUserPlacement(){
		return userPlacement;
	}
	
	public void setUserPlacement(int userPlacement){
		this.userPlacement = userPlacement;
	}
	
	public void setHasStarted(boolean didstart) {
		hasStarted = didstart;
	}
	
	public boolean getHasStarted() {
		return hasStarted;
	}	

	public void setId(String id) {
		this.id = id;
	}

	public String getId() {
		return id;
	}
	
	public boolean getFormSent() {
		return formSent;
	}
	
	public void setFormSent(boolean isSent) {
		formSent = isSent;
	}
	
	public void setUsers(int users) {
		this.users = users;
	}

}
